using System;
using System.Threading;
using System.Xml;

using csammisrun.OscarLib;

using GSN.Config;

namespace GSN.Senders
{
    public class ICQSMSSender : ISMSSender
    {
        private string _uin = null;
        private string _password = null;
        private string _icqServer = "login.icq.com";
        private int _icqPort = 5190;

        private EventWaitHandle sema = null;
        private Exception lastError = null;
        private Session s = null;

        public ICQSMSSender()
        {

        }

        // for old versions
        public ICQSMSSender(string uin, string password)
        {            
            _uin = uin;
            _password = password;
        }

        ~ICQSMSSender()
        {
            if (sema != null)
            {
                sema.Close();
                sema = null;
            }

            s = null;
        }



        public void load(XmlPasswordNode node)
        {
            XmlPasswordNode n = node.SelectSingleNode("Username");
            if (n != null)
                _uin = n.InnerText;

            n = node.SelectSingleNode("Password");
            if (n != null)
            {
                _password = n.EncryptedInnerText;
                /*if (bool.Parse(GSNConfig.getConfigItem(GSNConfig.GSN_CONFIG_PASSWORDS_ENCRYPTED)))
                {
                    _password = CryptUtils.decrypt(_password);
                }*/
            }

            n = node.SelectSingleNode("Server");
            if (n != null)
                _icqServer = n.InnerText;
            else
                _icqServer = "login.icq.com";

            n = node.SelectSingleNode("Port");
            _icqPort = 5190;
            if (n != null)
            {
                int.TryParse(n.InnerText, out _icqPort);
            }

        }

        public void save(XmlPasswordWriter writer)
        {
            writer.WriteElementString("Username", _uin);
            writer.WritePasswordElementString("Password", _password);
            writer.WriteElementString("Server", _icqServer);
            writer.WriteElementString("Port", _icqPort.ToString());

            /*if (bool.Parse(GSNConfig.getConfigItem(GSNConfig.GSN_CONFIG_PASSWORDS_ENCRYPTED)))
                writer.WriteElementString("Password", CryptUtils.encrypt(_password));
            else
                writer.WriteElementString("Passowrd", _password);*/
        }
        

        public void start()
        {
            // @@@ not sure if to put it here or in constructor.
            sema = new EventWaitHandle(false, EventResetMode.AutoReset);

            lastError = null;

            // @@@ temp for debugging
            bool oscarLog = false;
            try
            {
                oscarLog = (GSNConfig.getConfigItem("GSN/Settings/ICQ/OscarLog") == "1");
            }
            catch (Exception)
            {
            }
            
            s = new Session(_uin, _password, oscarLog);

            s.LoginCompleted += new LoginCompletedHandler(s_LoginCompleted);
            s.SmsMessageReceived += new SmsMessageReceivedHandler(s_SmsMessageReceived);
            s.LoginFailed += new LoginFailedHandler(s_LoginFailed);
            s.SmsMessageError += new SmsMessageErrorHandler(s_SmsMessageError);
            s.LoginStatusUpdate += new LoginStatusUpdateHandler(s_LoginStatusUpdate);
            Logger.log("ICQ: Starting Login...");

            // probably will never change (famous last words...)
            //s.Logon("login.icq.com", 5190);
            s.Logon(_icqServer, _icqPort);

            Logger.log("ICQ: Waiting For Login Response ({0}:{1})...", _icqServer, _icqPort);

            // wait for login complete or fail
            if (!sema.WaitOne(30 * 1000, false))
            {
                // timeouted, something went wrong
                if (s.LoggedIn)
                    s.Logoff();

                throw new Exception("Sender.Start(): Timeout Error");
            }

            // check for error
            if (lastError != null)
                throw lastError;

            Logger.log("ICQ: Logged in.");

        }

        void s_LoginStatusUpdate(Session sess, string message, double percentdone)
        {
            Logger.log(string.Format("ICQ Login Update: {0} ({1}).", message, percentdone));
        }

        public void stop()
        {
            if (s.LoggedIn)
                s.Logoff();

            // wait a sec for icq to logoff
            System.Threading.Thread.Sleep(1000);

            // clear session
            s = null;
            
            if (sema != null)
            {
                sema.Close();
                sema = null;
            }
        }

        public void send(string recipient, string text)
        {
            lastError = null;

            s.SendSMSMessage(recipient, text, "GSN");

            Logger.log("ICQ: Waiting for SMS Send...");

            // wait for sending to be over            
            if (!sema.WaitOne(30 * 1000, false))
            {
                if (s.LoggedIn)
                    s.Logoff();

                throw new Exception("Sender.Start(): Timeout Error");
            }

            // check for error
            if (lastError != null)
            {
                if (s.LoggedIn)
                    s.Logoff();
                throw lastError;
            }

            Logger.log("ICQ: SMS Sent.");

        }

        private void s_SmsMessageError(Session sess, string error, string param)
        {
            lastError = new Exception("ICQ SMS Error: " + error + " param: " + param);
            sema.Set();
        }

        void s_LoginFailed(Session sess, LoginErrorCode errorcode)
        {
            Logger.log("ICQ: Logging failed, setting exception.");
            lastError = new Exception("ICQ Login Failed: " + errorcode.ToString());
            sema.Set();
        }

        void s_SmsMessageReceived(Session sess, string messageId)
        {
            Logger.log("ICQ: SMS Sent Successfully.");
            sema.Set();            
        }
        
        void s_LoginCompleted(Session sess)
        {
            Logger.log("ICQ: Login Completed.");
            sema.Set();   
        }

        

        public override bool Equals(object obj)
        {
            if (obj is ISMSSender)
            {
                if (obj is ICQSMSSender)
                {
                    ICQSMSSender s = (ICQSMSSender)obj;
                    if (s._uin == this._uin &&
                            s._password == this._password)
                    {
                        return true;
                    }
                }
                else
                    return false;

            }

            return false;
        }

        public override int GetHashCode()
        {
            return (_uin.GetHashCode() + _password.GetHashCode());
        }

        public override string ToString()
        {
            return "ICQ (Free)";
        }

        public bool showConfigDialog(IConfigurator configurator)
        {
            configurator.setItemString(_uin, "Username");
            configurator.setItemString(_password, "Password");
            configurator.setItemString(_icqServer, "Server");
            configurator.setItemString(_icqPort.ToString(), "Port");

            if (configurator.show())
            {
                _uin = configurator.getItemString("Username");
                _password = configurator.getItemString("Password");
                _icqServer = configurator.getItemString("Server");
                _icqPort = int.Parse(configurator.getItemString("Port"));

                return true;
            }

            return false;
        }

        public string getConfiguratorTypeName()
        {
            return "GSNConf.Senders.ICQSMSSenderConfigurator";
        }

    }
}
